/*
 *  Copyright 2002 by Texas Instruments Incorporated.
 *  All rights reserved. Property of Texas Instruments Incorporated.
 *  Restricted rights to use, duplicate or disclose this code are
 *  granted through contract.
 *  
 */
/*
 *  ======== celldiff.c ========
 */                                          
                        
#include <std.h>
#include <csl_dat.h>
#include <algrf.h>
#include <icell.h>
#include <utl.h>

#include "cellDiff.h"
#include "appResources.h"
#include "appThreads.h"

#pragma DATA_SECTION(yOutBuff, ".EXTPROCBUFF");
#pragma DATA_SECTION(crOutBuff, ".EXTPROCBUFF");
#pragma DATA_SECTION(cbOutBuff, ".EXTPROCBUFF");
#pragma DATA_ALIGN(yOutBuff, MEMALIGN);
#pragma DATA_ALIGN(crOutBuff, MEMALIGN);
#pragma DATA_ALIGN(cbOutBuff, MEMALIGN);

//Temporary frame buffers to hold output data
static Char yOutBuff[PROCF_SIZE_IN_PIXELS];
static Char crOutBuff[PROCF_SIZE_IN_PIXELS << 2];
static Char cbOutBuff[PROCF_SIZE_IN_PIXELS << 2];

static void runDIFF( IDIFF_Handle handle, Short **inData,
                       Uint32 **outData, DIFF_Env * env);

// v-table for this cell
ICELL_Fxns DIFF_CELLFXNS = { 
    NULL,                  // cellClose
    NULL,                  // cellControl
    DIFF_cellExecute,      // cellExecute
    NULL                   // cellOpen
};

/*
 *  ======== DIFF_cellExecute ========
 *
 */
Bool DIFF_cellExecute( ICELL_Handle handle, Arg arg )
{    
    IDIFF_Handle diffHandle = (IDIFF_Handle)handle->algHandle;

    // activate instance object
    ALGRF_activate( handle->algHandle );

    runDIFF(diffHandle,
              (Short **)handle->inputIcc[0]->buffer,
              (Uint32 **)handle->outputIcc[0]->buffer,
              (DIFF_Env *)handle->cellEnv);

    // deactivate instance object
    ALGRF_deactivate( handle->algHandle );

    return(TRUE);
}    

/*
 *  ======== runDIFF ========
 *  Run DIFF algorithm.
 */
static void runDIFF(IDIFF_Handle handle, Short **inData, 
                      Uint32 **outData, DIFF_Env *env)
{   
    Int toggle = 0;       // used for double buffering     
    Int yInId, crInId, cbInId; // ids for current DMA transfers
    Int yOutId, crOutId, cbOutId; // ids for current DMA transfers
    Int yPrevInId, crPrevInId, cbPrevInId; // prev. submitted transfers
    Int yPrevOutId, crPrevOutId, cbPrevOutId; // prev. submitted transfers
    int finalOutId; // final transfer to display buffer
    Int i;
    
    // Data pointers for incoming data
    Char *yInData  = (Char *)inData[0];
    Char *crInData = (Char *)inData[1];
    Char *cbInData = (Char *)inData[2];

    // Data pointers for outgoing data
    Char *yOutData;
    Char *crOutData;
    Char *cbOutData;
    
    // Data pointers for intermediate data
    Char *intYBuf  = env->intYBuf;
    Char *intCrBuf = env->intCrBuf;
    Char *intCbBuf = env->intCbBuf;
    
    // Data pointers for previous frame (reference frame)
    Char *ptrPrevY = env->prevY;
    Char *ptrPrevCr = env->prevCr;
    Char *ptrPrevCb = env->prevCb;

	// Processing block parameters
    Uns numBlocks       = env->numBlocks;
    Uns ySingleBufSize  = env->yBufSize >> 1;
    Uns crSingleBufSize = env->crBufSize >> 1;
    Uns cbSingleBufSize = env->cbBufSize >> 1;
	Int linePitch		= env->linePitch;
   
	// Point output data to intermediate buffers
	yOutData = yOutBuff; 
	crOutData = crOutBuff;
	cbOutData = cbOutBuff;

    
    // Prime the DMA to get initial transfer ids
    yPrevInId   = DAT_copy((Void *)yInData,  (Void *)intYBuf,  ySingleBufSize);
    crPrevInId  = DAT_copy((Void *)crInData, (Void *)intCrBuf, crSingleBufSize);
    cbPrevInId  = DAT_copy((Void *)cbInData, (Void *)intCbBuf, cbSingleBufSize);
        
    // Needed for the first operation in a pipelined copy sequence.
    yPrevOutId  = DAT_XFRID_WAITNONE;
    crPrevOutId = DAT_XFRID_WAITNONE;
    cbPrevOutId = DAT_XFRID_WAITNONE;

    for ( i = 0; i < numBlocks; i++) {

        /*
         *  Update external input buffer pointers
         *  Note: One of the transfers was initiated before this
         *        for loop. Therefore you need only numBlocks - 1
         *        transfers in this loop.
         */
        if (i < (numBlocks - 1)) {
            yInData  += ySingleBufSize;
            crInData += crSingleBufSize;
            cbInData += cbSingleBufSize;
    
            // DMA next set of input data into internal buffers
            yInId  = DAT_copy((Void *)yInData, 
                        (Void *)(intYBuf + ((toggle ^ 1) * ySingleBufSize)),
                        ySingleBufSize);
            crInId = DAT_copy((Void *)crInData,
                        (Void *)(intCrBuf + ((toggle ^ 1) * crSingleBufSize)),
                        crSingleBufSize);
            cbInId = DAT_copy((Void *)cbInData,
                        (Void *)(intCbBuf + ((toggle ^ 1) * cbSingleBufSize)),
                        cbSingleBufSize);
        }

        // Wait on previous DMA transfer
        DAT_wait(yPrevInId);
        DAT_wait(crPrevInId);
        DAT_wait(cbPrevInId);
        
        // Now for the real work...call the algorithm.
        handle->fxns->apply(handle, 
            (unsigned char *)(intYBuf  + (toggle * ySingleBufSize)),
            (unsigned char *)(intCrBuf + (toggle * crSingleBufSize)),
            (unsigned char *)(intCbBuf + (toggle * cbSingleBufSize)),
            (unsigned char *)ptrPrevY, (unsigned char *)ptrPrevCr,
            (unsigned char *)ptrPrevCb, ySingleBufSize, crSingleBufSize,
            env->yValue, env->crValue, env->cbValue, PROCF_WIDTH);

        // DMA decompressed data out to external memory
        yOutId = DAT_copy((Void *)(intYBuf + (toggle * ySingleBufSize)),
                          (Void *) yOutData, ySingleBufSize);
        crOutId = DAT_copy((Void *)(intCrBuf + (toggle * crSingleBufSize)),
                           (Void *) crOutData, crSingleBufSize);
        cbOutId = DAT_copy((Void *)(intCbBuf + (toggle * cbSingleBufSize)),
                           (Void *) cbOutData, cbSingleBufSize);
       
        // Update external output buffer pointers
        yOutData  += ySingleBufSize;
        crOutData += crSingleBufSize;
        cbOutData += cbSingleBufSize;
        
        // Update reference frame pointers
        ptrPrevY += ySingleBufSize;
        ptrPrevCr += crSingleBufSize;
        ptrPrevCb += cbSingleBufSize;

        toggle ^= 1;

        /*
         *  Wait on previous DMA transfers.  
         *  Note: The first time, there is no previous transfer to wait on.
         *        The last set of DAT_wait is outside the for loop.
         */ 
        DAT_wait(yPrevOutId);
        DAT_wait(crPrevOutId);
        DAT_wait(cbPrevOutId);
        
        yPrevInId   = yInId;
        crPrevInId  = crInId;
        cbPrevInId  = cbInId;

        yPrevOutId  = yOutId;
        crPrevOutId = crOutId;
        cbPrevOutId = cbOutId;
    }

    // Wait on last set of DMA transfers    
    DAT_wait(yPrevOutId);
    DAT_wait(crPrevOutId);
    DAT_wait(cbPrevOutId);
    
	DAT_copy2d(DAT_1D2D, yOutBuff, outData[0], PROCF_WIDTH, PROCF_HEIGHT,linePitch);
	DAT_copy2d(DAT_1D2D, crOutBuff, outData[1], PROCF_WIDTH>>1, PROCF_HEIGHT>>1,linePitch>>1);
	finalOutId = DAT_copy2d(DAT_1D2D, cbOutBuff, outData[2], PROCF_WIDTH>>1, PROCF_HEIGHT>>1,linePitch>>1);
    
	DAT_wait(finalOutId);
}

 
